package net.jescort.repository.impl;

import net.gelif.modules.bbcode.BBProcessorFactory;
import net.gelif.modules.bbcode.TextProcessor;
import net.jescort.domain.forum.*;
import net.jescort.domain.user.User;
import net.jescort.persistence.dao.*;
import net.jescort.repository.EscortRepository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Service("escortRepository")
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class EscortRepositoryImpl implements EscortRepository
{
    protected transient final Log logger = LogFactory.getLog(this.getClass());

    public static TextProcessor processor = BBProcessorFactory.getInstance().create();

    @Resource(name = "attachmentDao")
    private AttachmentDao attachmentDao;

    @Resource(name = "forumDao")
    private ForumDao forumDao;

    @Resource(name = "topicDao")
    private TopicDao topicDao;

    @Resource(name = "postDao")
    private PostDao postDao;

    @Resource(name = "messageDao")
    private MessageDao messageDao;

    @Override
    public Forum getForum(Integer forumId)
    {
        return forumDao.findOne(forumId);
    }

    @Override
    public boolean forumExists(Integer forumId)
    {
        return forumDao.exists(forumId);
    }

    @Override
    public Topic getTopic(Integer topicId)
    {
        return topicDao.findOne(topicId);
    }

    @Override
    public Topic getTopicWithBBCodeToHtml(Integer topicId)
    {
        Topic topic = topicDao.findOne(topicId);
        topic.setContent(processor.process(topic.getContent()));
        return topic;
    }

    @Transactional
    public void createTopic(Topic topic)
    {
        //TODO
        topicDao.save(topic);
    }

    @Transactional
    public void editTopic(Topic topic)
    {
        //TODO
        topicDao.save(topic);
    }

    @Override
    public Page<Topic> findTopicsByForum(Integer forumId, Pageable pageable)
    {
        List<Topic> topics = topicDao.findByForum(forumId, pageable);
        Page<Topic> page = new PageImpl<Topic>(topics);
        return page;
    }

    @Override
    public Page<Topic> findTopicsByForumWithBBCodeToHtml(Integer forumId, Pageable pageable)
    {
        List<Topic> topics = topicDao.findByForum(forumId, pageable);
        for(Topic topic : topics)
        {
            topic.setContent(processor.process(topic.getContent()));
        }
        Page<Topic> page = new PageImpl<Topic>(topics);
        return page;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ModelAndView topicView(Integer topicId, Integer pageNo, Integer pageSize, ModelAndView mav)
    {
        Topic topic = topicDao.findOne(topicId);
        topic.setContent(processor.process(topic.getContent()));
        Pageable pageable = new PageRequest(null != pageNo && pageNo > 0 ? pageNo : 0, null != pageSize && pageSize > 0 ? pageSize : 10);
        List<Post> posts = postDao.findByTopicId(topicId, pageable);
        for(Post post : posts)
        {
            post.setContent(processor.process(post.getContent()));
        }
        Page<Post> page = new PageImpl<Post>(posts);
        topicDao.increaseViews(topicId);
        mav.addObject("topic", topic);
        mav.addObject("posts", page);
        return mav;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void replyTopic(final Post post, HttpServletRequest request)
    {
        //post.setPoster(new User(1));
        post.setEdits(0);
        post.setCreatedate(Calendar.getInstance());
        post.setEditdate(Calendar.getInstance());
        postDao.save(post);
        topicDao.increaseReplys(post.getTopicId());
        final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
        final Map<String, MultipartFile> multipartFiles = multipartRequest.getFileMap();
        for(final MultipartFile multipartFile : multipartFiles.values())
        {
            executor.execute(new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {
                        Attachment attachment = new Attachment();
                        attachment.setPostId(post.getId());
                        attachment.setOriginalName(multipartFile.getOriginalFilename());
                        attachment.setAttachment(multipartFile.getBytes());
                        attachment.setSize(multipartFile.getSize());
                        attachment.setCreatedate(Calendar.getInstance());
                        attachment.setContentType(multipartFile.getContentType());
                        attachmentDao.save(attachment);
                    }
                    catch(IOException e)
                    {
                        e.printStackTrace();
                        executor.shutdown();
                        try
                        {
                            if(!executor.awaitTermination(60, TimeUnit.SECONDS))
                            {
                                executor.shutdownNow();
                                if(!executor.awaitTermination(60, TimeUnit.SECONDS))
                                {
                                    logger.error("Pool did not terminate");
                                }
                            }
                        }
                        catch(InterruptedException ex)
                        {
                            executor.shutdownNow();
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            });
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void createPost(Post post)
    {
        post.setEdits(0);
        post.setCreatedate(Calendar.getInstance());
        post.setEditdate(Calendar.getInstance());
        postDao.save(post);
    }

    @Override
    public Post getPost(Integer postId)
    {
        return postDao.findOne(postId);
    }

    @Override
    public Post getPostWithBBCodeToHtml(Integer postId)
    {
        Post post = postDao.findOne(postId);
        post.setContent(processor.process(post.getContent()));
        return post;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void editPost(Post post)
    {
        //TODO
        postDao.save(post);
    }

    @Override
    public Post quotePost(Integer postId)
    {
        Post post = postDao.findOne(postId);
        Post quotePost = new Post();
        StringBuffer sb = new StringBuffer();
        sb.append("[quote=");
        sb.append(post.getPoster().getUsername());
        sb.append("]");
        sb.append(post.getContent());
        sb.append("[/quote]");
        quotePost.setContent(sb.toString());
        return quotePost;
    }

    @Override
    public Page<Post> findPostsByTopicWithBBCodeToHtml(Integer topicId, Pageable pageable)
    {
        List<Post> posts = postDao.findByTopicId(topicId, pageable);
        for(Post post : posts)
        {
            post.setContent(processor.process(post.getContent()));
        }
        Page<Post> page = new PageImpl<Post>(posts);
        return page;
    }

    @Override
    public Message getMessage(Integer messageId)
    {
        return messageDao.findOne(messageId);
    }

    @Override
    public Page<Message> findMessagesBySender(Integer senderId, Pageable pageable)
    {
        List<Message> messages = messageDao.findBySenderId(senderId, pageable);
        Page<Message> page = new PageImpl<Message>(messages);
        return page;
    }

    @Override
    public Page<Message> findMessagesByRecipient(Integer recipientId, Pageable pageable)
    {
        List<Message> messages = messageDao.findByRecipientId(recipientId, pageable);
        Page<Message> page = new PageImpl<Message>(messages);
        return page;
    }

    @Override
    public Attachment getAttachment(Integer attachmentId)
    {
        return attachmentDao.findOne(attachmentId);
    }

    @Override
    public void saveAttachment(Attachment attachment)
    {
        attachmentDao.save(attachment);
    }
}
